//
//  NEDCountDownTool.m
//  MoreCoin
//
//  Created by 林宁宁 on 2020/3/17.
//  Copyright © 2020 MoreCoin. All rights reserved.
//

#import "NEDCountDownTool.h"
#import "NEDConstant.h"

typedef NS_ENUM(NSInteger, NEDTimerType) {
    NEDTimerType_CountDown,//倒计时
    NEDTimerType_Repeat,//重复执行
    NEDTimerType_Delay,//延迟执行
};

@interface NEDCountDownItem : NSObject

@property (weak, nonatomic) id target;
@property (copy, nonatomic) NSString * identifyType;
@property (assign, nonatomic) NEDTimerType type;
@property (assign, nonatomic) NSTimeInterval step;
@property (assign, nonatomic) NSTimeInterval currentStep;

@property (assign, nonatomic) NSTimeInterval duration;
@property (assign, nonatomic) NSTimeInterval remain;
@property (copy, nonatomic) void(^progressBlock)(CGFloat total, CGFloat remain, id target);
@property (copy, nonatomic) void(^finishBlock)(id target);

/// 重复次数 -1就是无限循环
@property (assign, nonatomic) CGFloat repeatTimes;
@property (assign, nonatomic) CGFloat currentRepeatTimes;
@property (copy, nonatomic) void(^repeatBlock)(CGFloat repeatTimes, id target);

@end

@implementation NEDCountDownItem


@end




@interface NEDCountDownTool()

@property (retain, nonatomic) NSMutableArray <NEDCountDownItem *>* dataList;
@property (retain, nonatomic) NSTimer * timer;

/// 定时器的执行间隔 0.01
@property (assign, nonatomic) CGFloat timerStep;

@end


@implementation NEDCountDownTool

+ (instancetype)share{
    static NEDCountDownTool * tool = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        tool = [NEDCountDownTool new];
        tool.dataList = [NSMutableArray new];
        tool.timerStep = 0.01;
    });
    return tool;
}


/// 倒计时
/// @param target 关联的对象 该对象释放也跟着释放
/// @param duration 时长 秒
/// @param progressBlock 开始倒计时回调
/// @param finishBlock 完成
+ (void)ned_addCountdownTarget:(id _Nonnull)target
                     duration:(CGFloat)duration
                         step:(CGFloat)step
                     progress:(void(^)(CGFloat total, CGFloat remain, id target))progressBlock
                       finish:(void(^ _Nullable)(id target))finishBlock{
    [NEDCountDownTool ned_addCountdownTarget:target identify:nil duration:duration step:step progress:progressBlock finish:finishBlock];
}

+ (void)ned_addCountdownTarget:(id _Nonnull)target
                      identify:(NSString * _Nullable)identify
                     duration:(CGFloat)duration
                         step:(CGFloat)step
                     progress:(void(^_Nullable)(CGFloat total, CGFloat remain, id target))progressBlock
                        finish:(void(^ _Nullable)(id target))finishBlock{
    NEDCountDownItem * item = [NEDCountDownItem new];
    item.type = NEDTimerType_CountDown;
    if(step <= 0){
        item.step = 1;
    }else{
        item.step = step;
    }
    item.identifyType = identify;
    item.step = MAX(item.step, [NEDCountDownTool share].timerStep);
    item.target = target;
    item.duration = duration;
    item.remain = duration;
    item.progressBlock = progressBlock;
    item.finishBlock = finishBlock;
    
    [[NEDCountDownTool share] addCountDown:item];
}

/// 延迟执行
+ (void)ned_addDelayTarget:(id _Nonnull)target
                  duration:(CGFloat)duration
                    finish:(void(^ _Nullable)(id target))finishBlock{
    [NEDCountDownTool ned_addDelayTarget:target identify:nil duration:duration finish:finishBlock];
}

+ (void)ned_addDelayTarget:(id _Nonnull)target
                  identify:(NSString * _Nullable)identify
                  duration:(CGFloat)duration
                    finish:(void(^ _Nullable)(id target))finishBlock{
    NEDCountDownItem * item = [NEDCountDownItem new];
    item.type = NEDTimerType_Delay;
    item.identifyType = identify;
    item.step = [NEDCountDownTool share].timerStep;
    item.target = target;
    item.duration = duration;
    item.remain = duration;
    item.finishBlock = finishBlock;
    
    [[NEDCountDownTool share] addCountDown:item];
}

+ (void)ned_addCountdownTarget:(id)target
                     duration:(CGFloat)duration
                     progress:(void(^)(CGFloat total, CGFloat remain, id target))progressBlock
                       finish:(void(^ _Nullable)(id target))finishBlock{
    if(duration <= 0){
        if(finishBlock){
            finishBlock(target);
        }
        return;
    }
    [NEDCountDownTool ned_addCountdownTarget:target duration:duration step:1 progress:progressBlock finish:finishBlock];
}

/// 重复执行
/// @param repeatCount 重复次数 -1就是无限循环
/// @param step 时间间隔
/// @param progressBlock 重复一次回调
+ (void)ned_addRepeatTarget:(id _Nonnull)target
               repeatCount:(CGFloat)repeatCount
                      step:(CGFloat)step
                  progress:(void(^)(CGFloat repeatTimes, id target))progressBlock{
    
    [NEDCountDownTool ned_addRepeatTarget:target identify:nil repeatCount:repeatCount step:step progress:progressBlock];
}

+ (void)ned_addRepeatTarget:(id _Nonnull)target
                   identify:(NSString * _Nullable)identify
               repeatCount:(CGFloat)repeatCount
                      step:(CGFloat)step
                   progress:(void(^_Nullable)(CGFloat repeatTimes, id target))progressBlock{
    NEDCountDownItem * item = [NEDCountDownItem new];
    item.type = NEDTimerType_Repeat;
    if(step <= 0){
        item.step = 1;
    }else{
        item.step = step;
    }
    item.identifyType = identify;
    item.step = MAX(item.step, [NEDCountDownTool share].timerStep);
    item.target = target;
    item.repeatTimes = repeatCount;
    item.repeatBlock = progressBlock;
    
    [[NEDCountDownTool share] addCountDown:item];
}

+ (void)ned_cleanCountdownTarget:(id)target{
    [[NEDCountDownTool share] cleanCountDownByTarget:target];
}

+ (void)ned_cleanCountdownIdentify:(NSString *)identify{
    [[NEDCountDownTool share] cleanCountDownByIdentify:identify];
}

- (void)addCountDown:(NEDCountDownItem *)item{
    
    if(item.type == NEDTimerType_CountDown){
        if(item.progressBlock){
            item.progressBlock(item.duration,item.remain,item.target);
        }
    }else if (item.type == NEDTimerType_Repeat){
        if(item.repeatBlock){
            item.repeatBlock(item.currentRepeatTimes,item.target);
        }
    }
    
    [self.dataList addObject:item];
    
    [self restartTimer];
}

- (void)removeCountDown:(NEDCountDownItem *)item{
    
    [self.dataList removeObject:item];
    
    [self restartTimer];
}

- (void)cleanCountDownByTarget:(id)target{
    
    [self.dataList enumerateObjectsUsingBlock:^(NEDCountDownItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
       if(obj.target && [obj.target isEqual:target]){
           [self.dataList removeObject:obj];
       }
    }];
    
    [self restartTimer];
}

- (void)cleanCountDownByIdentify:(NSString *)identify{
    if(IsNull(identify)){
        return;
    }
    [self.dataList enumerateObjectsUsingBlock:^(NEDCountDownItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
       if(IsNonNull(obj.identifyType) &&
          [obj.identifyType isEqualToString:identify]){
           [self.dataList removeObject:obj];
       }
    }];
    
    [self restartTimer];
}

- (void)restartTimer{
    if(self.dataList.count > 0){
        if(!self.timer){
            [self timerBegin];
        }
    }else{
        [self timerClean];
    }
}

- (void)timerBegin{
    
    [self timerClean];
    
    self.timer = [NSTimer scheduledTimerWithTimeInterval:self.timerStep target:self selector:@selector(timerUpdate) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    
}

- (void)timerClean{
    
    if(self.timer){
        [self.timer invalidate];
        self.timer = nil;
        
        NSLog(@"倒计时 timer 释放计时器");
    }
}


- (void)timerUpdate{
    
    [self.dataList enumerateObjectsUsingBlock:^(NEDCountDownItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
       
        if(IsNull(obj.target)){
            [self removeCountDown:obj];
        }
    }];
    
    [self.dataList enumerateObjectsUsingBlock:^(NEDCountDownItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
       
        obj.currentStep = obj.currentStep + self.timerStep;
        
        if(obj.type == NEDTimerType_CountDown){
            obj.remain = obj.remain - self.timerStep;
            obj.remain = MAX(0, obj.remain);
            
            if(obj.currentStep >= obj.step){
                if(obj.progressBlock){
                    obj.progressBlock(obj.duration, obj.remain,obj.target);
                }
                obj.currentStep = 0;
            }
            
            if(obj.remain <= 0){
                if(obj.finishBlock){
                    obj.finishBlock(obj.target);
                }
                [self removeCountDown:obj];
            }
            
        }
        else if (obj.type == NEDTimerType_Repeat){
            obj.remain = obj.remain + self.timerStep;
            
            if(obj.currentStep >= obj.step){
                obj.currentRepeatTimes = obj.currentRepeatTimes + 1;
                if(obj.repeatBlock){
                    obj.repeatBlock(obj.currentRepeatTimes,obj.target);
                }
                obj.currentStep = 0;
            }
            
            if(obj.repeatTimes > 0 &&
               obj.currentRepeatTimes >= obj.repeatTimes){
                [self removeCountDown:obj];
            }
        }
        else if (obj.type == NEDTimerType_Delay){
            obj.remain = obj.remain - self.timerStep;
            obj.remain = MAX(0, obj.remain);
            if(obj.remain <= 0){
                if(obj.finishBlock){
                    obj.finishBlock(obj.target);
                }
                [self removeCountDown:obj];
            }
        }
        
    }];

}





@end
